package com.jeasonzhao.commons.parser.expression;

import java.util.Vector;

import com.jeasonzhao.commons.parser.lex.BaseTokenizer;
import com.jeasonzhao.commons.parser.lex.ExpressionTokenizer;
import com.jeasonzhao.commons.parser.lex.LexException;
import com.jeasonzhao.commons.parser.lex.LexToken;
import com.jeasonzhao.commons.parser.lex.LexTokenCollection;
import com.jeasonzhao.commons.parser.lex.LexTokenTypes;

public class ExpressionParser
{
    private boolean m_isCaseSensitive = false;
    private static final String[] m_strOperations = new String[]
        {
        "and","or","not","equals", //Logical Operation
        "||","&&",">=","<=","==","!=",
        "<>",">","<",
        "+","-","*","/","%",
    };
    private static java.util.HashSet<String> m_allOperations = null;
    private static java.util.HashSet<String> m_allOperationsIgnore = null;
    static
    {
        m_allOperations = new java.util.HashSet<String>();
        m_allOperationsIgnore = new java.util.HashSet<String>();
        for(int n = 0;null != m_strOperations && n < m_strOperations.length;n++)
        {
            m_allOperations.add(m_strOperations[n]);
            m_allOperationsIgnore.add(m_strOperations[n].toUpperCase());
        }
    }

    private LexTokenCollection m_allTokens = null;
    private int m_nIndex = 0;
    public ExpressionParser()
    {

    }

    private ExpressionParser(LexTokenCollection toks)
    {
        this();
        m_allTokens = toks;
        m_nIndex = 0;
    }

    public ExpressionParser(String str)
        throws LexException
    {
        this();
        setExpression(str);
    }

    public ExpressionParser(boolean isCaseSensitive)
    {
        this();
        m_isCaseSensitive = isCaseSensitive;
    }

    public ExpressionParser(boolean isCaseSensitive,String str)
        throws LexException
    {
        this();
        m_isCaseSensitive = isCaseSensitive;
        setExpression(str);
    }

    public void setExpression(String str)
        throws LexException
    {
        ExpressionTokenizer tk = new ExpressionTokenizer(str);
        m_allTokens = tk.getTokens(false,false);
        m_nIndex = 0;
    }

    public void setExpression(BaseTokenizer tk)
        throws LexException
    {
        m_allTokens = tk.getTokens(false,false);
        m_nIndex = 0;
    }

    private LexTokenCollection getNextBracketed()
        throws SyntaxException
    {
        if(m_allTokens == null || m_nIndex >= this.m_allTokens.size())
        {
            return null;
        }
        LexToken curToken = m_allTokens.elementAt(m_nIndex);
        LexToken nextToken = m_nIndex + 1 < m_allTokens.size() ? m_allTokens.elementAt(m_nIndex + 1) : null;
        if(curToken.isSpecial() && curToken.equals("("))
        {
            return parseBracket("(",")");
        }
        else if(curToken.isSpecial() && curToken.equals("["))
        {
            return parseBracket("[","]");
        }
        else if(curToken.isSpecial() && curToken.equals("{"))
        {
            return parseBracket("{","}");
        }
        else if(null != nextToken && curToken.isName() && nextToken.isSpecial() && nextToken.equals("(")) //function
        {
            m_nIndex++;
            boolean isKey = false;
            for(int n = 0;m_strOperations != null && n < m_strOperations.length;n++)
            {
                isKey = this.m_isCaseSensitive ? curToken.equals(m_strOperations[n]) :
                    curToken.getToken().equalsIgnoreCase(m_strOperations[n]);
                if(isKey)
                {
                    break;
                }
            }
            if(isKey)
            {
                LexTokenCollection lexGroup = new LexTokenCollection();
                lexGroup.add(curToken);
                return lexGroup;
            }
            else
            {
                LexTokenCollection group = parseBracket("(",")");
                group.insertElementAt(curToken,0);
                return group;
            }
        }
        else if(curToken.isSpecial() && curToken.equals(")"))
        {
            throwException(curToken,"Can not found the matched \")\"！");
            return null;
        }
        else if(curToken.isSpecial() && curToken.equals("]"))
        {
            throwException(curToken,"Can not found the matched \"]\"！");
            return null;
        }
        else if(curToken.isSpecial() && curToken.equals("}"))
        {
            throwException(curToken,"Can not found the matched \"}\"！");
            return null;
        }
        else //single
        {
            m_nIndex++;
            LexTokenCollection lexGroup = new LexTokenCollection();
            lexGroup.add(curToken);
            return lexGroup;
        }
    }

    private LexTokenCollection parseBracket(String strBegin,String strEnd)
        throws SyntaxException
    {
        LexToken tokenFirst = m_allTokens.elementAt(m_nIndex);
        m_nIndex++;
        int nDeep = 1;
        LexTokenCollection lexGroup = new LexTokenCollection();
        lexGroup.add(tokenFirst);
        while(m_nIndex < m_allTokens.size())
        {
            LexToken token = m_allTokens.elementAt(m_nIndex);
            m_nIndex++;
            if(token.equals(strEnd))
            {
                nDeep--;
                lexGroup.add(token);
                if(nDeep == 0)
                {
                    break;
                }
            }
            else if(token.equals(strBegin))
            {
                lexGroup.add(token);
                nDeep++;
            }
            else
            {
                lexGroup.add(token);
            }
        }
        if(nDeep != 0)
        {
            throwException(tokenFirst,"Can not found the matched bracket！");
        }
        return lexGroup;
    }

    private void throwException(LexToken token,String strMessage)
        throws SyntaxException
    {
        throw new SyntaxException("Line at " + (token.getBeginRowIndex() + 1) +
                                  " and Column at " + (token.getBeginColumn() + 1) + " ,"
                                  + strMessage);
    }

    public SyntaxNode parseNode()
        throws SyntaxException
    {
        return parseList(getAllBracketed());
    }

    private Vector<LexTokenCollection> getAllBracketed()
        throws SyntaxException
    {
        Vector<LexTokenCollection> vector = new Vector<LexTokenCollection>();
        boolean isLastNodeIsOperation = true;
        LexTokenCollection nextB = null;
        //按照括号进行拆分,这里需要注意的是，在进行词法分析的时候将单词尽可能的拆分，
        //例如1+2直接拆分成1 ||| +2这样需要强制将数字的符号抢回来
        while((nextB = getNextBracketed()) != null)
        {
            boolean isOpThis = nextB.size() == 1 &&
                nextB.get(0).isComment() == false &&
                nextB.get(0).isString() == false &&
                nextB.get(0).getToken() != null &&
                (this.m_isCaseSensitive ? m_allOperations.contains(nextB.get(0).getToken()) :
                 m_allOperationsIgnore.contains(nextB.get(0).getToken().toUpperCase()));
            if(false == isOpThis
               && isLastNodeIsOperation == false
               && nextB.size() == 1 && nextB.get(0).isNumeric()
               && nextB.get(0).getToken() != null
               && "+-".indexOf(nextB.get(0).getToken().charAt(0)) >= 0
                )
            {
                //拆分第一个
                char c = nextB.get(0).getToken().charAt(0);
                LexToken newT = LexToken.create("" + c);
                newT.setType(LexTokenTypes.SPECIAL);
                LexTokenCollection newG = new LexTokenCollection();
                newG.add(newT);
                vector.add(newG);
                nextB.get(0).setToken(nextB.get(0).getToken().substring(1));
                vector.add(nextB);
//                System.out.println("   拆分：" + newG.toString());
//                System.out.println("   拆分：" + nextB.toString());
            }
            else
            {
                vector.add(nextB);
//                System.out.println("   拆分：" + g.toString(" ",false));
            }
            isLastNodeIsOperation = isOpThis;
        }
        return vector;
    }

    private Vector<LexTokenCollection> subListVector(Vector<LexTokenCollection> vI,int n1,int n2)
    {
        Vector<LexTokenCollection> v = new Vector<LexTokenCollection>();
        v.addAll(vI.subList(n1,n2));
        return v;
    }

    private SyntaxNode parseList(Vector<LexTokenCollection> listCols)
        throws SyntaxException
    {
//        System.out.println("分析>>>>" + this.getListString(listCols));
        int nIndex = 0;
        for(int n = 0;m_strOperations != null && n < m_strOperations.length;n++)
        {
            //查找操作符号
            nIndex = findTokenReverse(listCols,m_strOperations[n]);
            if(nIndex >= 0)
            {
                break;
            }
        }
        if(nIndex < 0)
        {
            nIndex = findTokenReverseSign(listCols);
//            if(nIndex>0)
//            {
//                System.out.println(this.getListString(listCols));
//            }
        }
        if(nIndex < 0)
        {
            return getSingleNode(listCols);
        }
        else if(nIndex == 0)
        {
            LexToken tk = ((LexTokenCollection) listCols.get(0)).get(0);
            String op = tk.getToken();
            if(listCols.size() < 2)
            {
                throwException(tk,"缺少操作符配对的左子和右子！");
            }
            LexToken tk2 = ((LexTokenCollection) listCols.get(1)).get(0);
            if("+-".indexOf(op) >= 0)
            {
                if(tk2.isNumeric() == false
                   || (null != tk2.getPrefix() && tk2.getPrefix().length() > 0)
                   || tk2.getToken().startsWith("+")
                   || tk2.getToken().startsWith("-"))
                {
                    throwException(tk,op + "不能叠加到 " + tk2.getInitString());
                }
                listCols.remove(0);
                tk2.moveToPrevToken(tk);
                return getSingleNode(listCols);
            }
            else
            {
                throwException(tk,"缺少操作符左子！");
            }
            return null;
        }
        else
        {
            LexToken token = ((LexTokenCollection) listCols.get(nIndex)).get(0);
            Vector<LexTokenCollection> left = subListVector(listCols,0,nIndex);
            Vector<LexTokenCollection> right = subListVector(listCols,nIndex + 1,listCols.size());
//            System.out.println("操作符分析>>["+token+"]>>Left>>>" + this.getListString(left));
//            System.out.println("操作符分析>>["+token+"]>>Right>>>" + this.getListString(right));
            if(left.size() < 1)
            {
                throwException(token,"缺少操作符" + token + "的左子！");
            }
            if(right.size() < 1)
            {
                throwException(token,"缺少操作符" + token + "的右子！");
            }
            SyntaxOperationNode node = new SyntaxOperationNode(parseList(left)
                ,token.getToken()
                ,parseList(right));
            return wrapNode(node);
        }
    }

    private SyntaxNode getSingleNode(Vector<LexTokenCollection> listCols)
        throws SyntaxException
    {
        if(null == listCols || listCols.size() < 1)
        {
            return null;
        }
        LexTokenCollection col = unite(listCols);
        if(listCols.size() > 1)
        {
            throwException(col.get(0),"Sub-Expression[" + this.getListString(listCols) + "] has some errors.");
        }
        if(col.size() < 1)
        {
            return null;
        }
        if(col.size() > 1 && col.get(0).isName()) //函数处理
        {
            if(col.size() < 3)
            {
                throwException(col.get(0),"函数定义不完整[" + col.toString() + "]");
            }
            return getFunctionNode(col);
        }
        else if(col.get(0).isSpecial() && col.get(0).equals("(")) //括号处理
        {
            if(col.size() < 2)
            {
                throwException(col.get(0),"括号定义不完整[" + col.toString() + "]");
            }
            else if(col.size() == 2)
            {
                throwException(col.get(0),"括号定义中没有内容");
            }
            ExpressionParser newSub = new ExpressionParser(col.slice(1,col.size() - 1));
            return wrapNode(new SyntaxBarackedNode(this.parseList(newSub.getAllBracketed())));
        }
        else if(col.get(0).isSpecial() && col.get(0).equals("[")) //括号处理
        {
            return getArrayNode(col);
        }
        else if(col.size() > 1)
        {
            throw new SyntaxException("语法分析错误[" + col.toString() + "]");
        }
        else //单一的节点处理
        {
            return wrapNode(new SyntaxValueNode(col.get(0).getType(),col.get(0).getToken()));
        }
    }

    private SyntaxNode getArrayNode(LexTokenCollection col)
        throws SyntaxException
    {
        LexTokenCollection ps = col.slice(1,col.size() - 1);
        if(ps.size() < 1)
        {
            return wrapNode(new SyntaxArrayNode());
        }
        return wrapNode(new SyntaxArrayNode(parseNodesSplitByComma(ps)));
    }

    private SyntaxNodeCollection parseNodesSplitByComma(LexTokenCollection ps)
        throws SyntaxException
    {
        if(null == ps || ps.size() < 1)
        {
            return null;
        }
        SyntaxNodeCollection array = new SyntaxNodeCollection();
        ExpressionParser newSub = new ExpressionParser(ps);
        Vector<LexTokenCollection> vector = newSub.getAllBracketed();
        //按照逗号分割
        while(true)
        {
            int nIndex = this.findTokenNormal(vector,",");
            if(nIndex < 0)
            {
                if(vector.size() > 0)
                {
                    array.addElement(newSub.parseList(vector));
                }
                break;
            }
            else
            {
                Vector<LexTokenCollection> vxs = subListVector(vector,0,nIndex);
                //找到了逗号
                if(vxs.size() < 1)
                {
                    throw new SyntaxException("语法分析错误【" + ps + "】，连续两个逗号！");
                }
//                System.out.println(newSub.getListString(vxs));
                array.addElement(newSub.parseList(vxs));
                vector = subListVector(vector,nIndex + 1,vector.size());
//                System.out.println("...."+newSub.getListString(vector));
                if(vector.size() < 1)
                {
                    throw new SyntaxException("语法分析错误【" + ps + "】，逗号后面无任何分割！");
                }

            }
        }
        return array;
    }

    private SyntaxNode getFunctionNode(LexTokenCollection col)
        throws SyntaxException
    {
        SyntaxFunctionNode node = new SyntaxFunctionNode(col.elementAt(0).getToken()
            ,this.parseNodesSplitByComma(col.slice(2,col.size() - 1)));
        return wrapNode(node);
    }

    private LexTokenCollection unite(Vector<LexTokenCollection> listCols)
    {
        if(null == listCols || listCols.size() < 1)
        {
            return null;
        }
        if(listCols.size() == 1)
        {
            return(LexTokenCollection) listCols.get(0);
        }
        else
        {
            LexTokenCollection ret = new LexTokenCollection();
            for(int n = 0;null != listCols && n < listCols.size();n++)
            {
                ret.addAll((LexTokenCollection) listCols.get(n));
            }
            return ret;
        }
    }

    private String getListString(Vector<LexTokenCollection> list)
    {
        StringBuffer str = new StringBuffer();
        for(int n = 0;null != list && n < list.size();n++)
        {
            LexTokenCollection g = (LexTokenCollection) list.get(n);
            str.append("◆" + g.toString());
        }
        return str.toString();
    }

    private int findTokenNormal(Vector<LexTokenCollection> list,String str)
    {
        if(list == null || str == null)
        {
            return -1;
        }
        for(int n = 0;n < list.size();n++)
        {
            LexTokenCollection g = (LexTokenCollection) list.get(n);
            if(g != null
               && g.size() == 1
               && g.get(0).isString() == false
               && g.get(0).isComment() == false
               && (m_isCaseSensitive ? g.get(0).equals(str) :
                   str.equalsIgnoreCase(g.get(0).getToken()))) //只检查单独的
            {
                return n;
            }
        }
        return -1;
    }

    private int findTokenReverse(Vector<LexTokenCollection> vector,String strOperation)
    {
        if(vector == null || strOperation == null) // ||vector.size()<=1)
        {
            return -1;
        }
        for(int nIndex = vector.size() - 1;nIndex >= 0;nIndex--)
        {
            LexTokenCollection g = (LexTokenCollection) vector.get(nIndex);
            if(g == null || g.size() != 1 || null == g.get(0))
            {
                continue;
            }
            LexToken tk = g.get(0);
//            System.out.println("check "+strOperation+" ==== "+tk);
            if(tk.isString() == false
               && tk.isComment() == false
               && (m_isCaseSensitive ? tk.equals(strOperation) : strOperation.equalsIgnoreCase(tk.getToken()))) //只检查单独的
            {
//                System.out.println("check "+strOperation+" ==== "+tk+"========================================OK");
                return nIndex;
            }
        }
        return -1;
    }

    private int findTokenReverseSign(Vector<LexTokenCollection> vector)
    {
        if(vector == null || vector.size() <= 1)
        {
            return -1;
        }
        //尝试从正数和负数中查找
        for(int nIndex = vector.size() - 1;nIndex >= 0;nIndex--)
        {
            LexTokenCollection g = (LexTokenCollection) vector.get(nIndex);
            if(g == null || g.size() != 1 || null == g.get(0))
            {
                continue;
            }
            LexToken tk = g.get(0);
            if(tk.isNumeric() && tk.getToken() != null && tk.getToken().length() > 0 &&
               "+-".indexOf(tk.getToken().charAt(0)) >= 0)
            {
                char c = tk.getToken().charAt(0);
                LexToken tkNew = LexToken.create(tk.getPosition(),tk.getBeginRowIndex(),tk.getBeginColumn());
                tkNew.setToken("" + c);
                tkNew.setType(com.jeasonzhao.commons.parser.lex.LexTokenTypes.SPECIAL);
                tkNew.setPrevBlanks(tk.getPrevBlanks());
                tkNew.setPrefix(tk.getPrefix());
                LexTokenCollection newC = new LexTokenCollection();
                newC.add(tkNew);
                vector.insertElementAt(newC,nIndex);
                tk.setPrefix(null);
                tk.setPrevBlanks(null);
                tk.setToken(tk.getToken().substring(1));
                tk.setBeginColumn(tk.getBeginColumn() + 1);
                tk.setPosition(tk.getPosition() + 1);
//                System.out.println(">>>>>>>>>>>>["+c+"]>>>>>>>>>>>>>   "  + this.getListString(vector));
                return nIndex;
            }
        }
        return -1;
    }

    protected SyntaxNode wrapNode(SyntaxNode node)
    {
        return node;
    }
    public static void main(String[] argvs)
        throws LexException,SyntaxException
    {
        try
        {
            String str = "sinXXXXX(1)+a";
//            BasicEvalProvider a = new BasicEvalProvider();
//            a.addVariable("ax",111.1);
//            ValuePair t = a.eval(str);
//            System.out.println(t.getDataType() + "=" + t.getValue());
            ExpressionParser p = new ExpressionParser(str);
            SyntaxNode node = p.parseNode();
            node.replaceVariableName("a","newA");
            System.out.println(node.toString());
//            node.eval()
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
}
